#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include "zlog.h"
#include "usr_cfg.h"
#include "usr_typedef.h"
#include "rs485_thread.h"
#include "main.h"
#include "modbus.h"


#define INVALID_SERVER_ID 18

const uint16_t UT_BITS_ADDRESS = 0x0;
const uint16_t UT_BITS_NB = 0;

const uint16_t UT_INPUT_BITS_ADDRESS = 0x0;
const uint16_t UT_INPUT_BITS_NB = 0;

const uint16_t UT_REGISTERS_ADDRESS = 0x0;
const uint16_t UT_REGISTERS_NB = 0x10;
const uint16_t UT_REGISTERS_NB_MAX = 0x20;
const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };

/* Raise a manual exception when this address is used for the first byte */
const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x170;
/* The response of the server will contains an invalid TID or slave */
const uint16_t UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE = 0x171;
/* The server will wait for 1 second before replying to test timeout */
const uint16_t UT_REGISTERS_ADDRESS_SLEEP_500_MS = 0x172;
/* The server will wait for 5 ms before sending each byte */
const uint16_t UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS = 0x173;

/* If the following value is used, a bad response is sent.
   It's better to test with a lower value than
   UT_REGISTERS_NB_POINTS to try to raise a segfault. */
const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2;

const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x0;
const uint16_t UT_INPUT_REGISTERS_NB = 0x80;		// 每通道16个特征值 * 8通道 共计128个



extern rs485_cfg_info		rs485_cfg;

#define SERVER_GPIO_INDEX   "511"


void rs485_de_gpio_ctl(modbus_t *ctx, int on)
{
	int dev_fd, ret;

	dev_fd = open("/sys/class/gpio/gpio416/value", O_RDWR);
	if(dev_fd == FAILURE) {
		perror("rs485_0 de gpio 416 dev open faield!");
		dzlog_error("rs485_0 de gpio 416 dev open faield!");
	}
	
	if (on)
		ret = write(dev_fd, "1", 1);
	else
		ret = write(dev_fd, "0", 1);
	
	if(ret == FAILURE) {
		perror("rs485_0 de gpio 416 dev write failed!");
		dzlog_error("rs485_0 de gpio 416 dev write failed!");
		close(dev_fd);
	}

	close(dev_fd);
}


//RS485服务器转entercat线程
void *daq_rs485_thread(void *arc)
{
	thread_status *s = NULL;
	
	char par;
	char filename[16];
	modbus_t *ctx;
    modbus_mapping_t *mb_mapping;
    int rc;
    int i;
    uint8_t *query;
    int header_length;

	printf("rs485 entercat convert daq thread is starting!\n");
	
	if(arc == NULL)
		return NULL;

	s = (thread_status*)arc;
	s->status = TS_ALIVE;

	(void)snprintf(filename, sizeof(filename), "/dev/%s", rs485_cfg.rs485_uart_cfg.com);
	switch (rs485_cfg.rs485_uart_cfg.parity)
	{
		case NONE :
			par = 'N';
			break;
		case ODD :
			par = 'O';
			break;
		case EVEN :
			par = 'E';
			break;
		default :
			par = 'N';
			break;
	}

    ctx = modbus_new_rtu(filename, rs485_cfg.rs485_uart_cfg.baudrate, par, 
					rs485_cfg.rs485_uart_cfg.bits, rs485_cfg.rs485_uart_cfg.stop);
	modbus_set_debug(ctx, TRUE);		// 打印太多，调试手动控制
	//modbus_set_debug(ctx, FALSE);
	header_length = modbus_get_header_length(ctx);
    modbus_set_slave(ctx, rs485_cfg.rs485_dev_id);

    // modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
    modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);
    modbus_rtu_set_custom_rts(ctx, rs485_de_gpio_ctl);

    printf("rs485 rts delay is %d\n", modbus_rtu_get_rts_delay(ctx));

    query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);			// 申请RTU最大的一帧空间

    // header_length = modbus_get_header_length(ctx);

    mb_mapping = modbus_mapping_new_start_address(
        		UT_BITS_ADDRESS, UT_BITS_NB,
        		UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,
        		UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX,
        		UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB);
    if (mb_mapping == NULL) {
        fprintf(stderr, "failed to allocate the mapping: %s\n", modbus_strerror(errno));
		dzlog_error("failed to allocate the mapping: %s!", modbus_strerror(errno));
        modbus_free(ctx);
		s->status = TS_TERMINATED;
		pthread_exit(arc);
    }

    /* Examples from PI_MODBUS_300.pdf.
       Only the read-only input values are assigned. */

    /* Initialize values of INPUT REGISTERS */
    for (i=0; i < UT_INPUT_REGISTERS_NB; i++) {
        mb_mapping->tab_input_registers[i] = i;
    }

    rc = modbus_connect(ctx);
    if (rc == -1) {
        fprintf(stderr, "rs485 modbus unable to connect %s\n", modbus_strerror(errno));
		dzlog_error("rs485 modbus unable to connect %s", modbus_strerror(errno));
        modbus_free(ctx);
		s->status = TS_TERMINATED;
		pthread_exit(arc);
    }

	while (s->status) 
	{
		if (rs485_cfg.rs485_en == ON)
		{
			if (rs485_cfg.rs485_modbus_en == ON)
			{
				do {
		            rc = modbus_receive(ctx, query);
		            // Filtered queries return 0
		        } while (rc == 0);

		        // The connection is not closed on errors which require on reply such as
		        //   bad CRC in RTU
		        if (rc == -1 && (errno != EMBBADCRC) && (errno != ETIMEDOUT)) {
		            break;
		        }

				/* Read input registers */
				if (query[header_length] == 0x04) {
					for (uint8_t ch = 0; ch < ACC_CH_TOTAL; ch++)
						for (uint16_t m = 0; m < sizeof(td_calc_norl) / sizeof(float); m++)
						mb_mapping->tab_input_registers[(ch * ACC_CH_TOTAL) + m] = \
						(int16_t)(*((float *)&acc_rt[ch].td_eigen_value + m) * 10 + 0.5f);	// 要用int型，有正负
				}
				
		        rc = modbus_reply(ctx, query, rc, mb_mapping);
		        if (rc == -1) {
		            break;
		        }
		        modbus_flush(ctx);
			}
		}else
		sleep(RS485_IDLE_DELAY);
	}

	printf("rs485_0 quit the loop: %s\n", modbus_strerror(errno));
    modbus_mapping_free(mb_mapping);
	
    /* For RTU */
    modbus_close(ctx);
    modbus_free(ctx);
	
	dzlog_notice("rs485_0 angle daq thread exit!!!");
	printf("rs485_0 angle daq thread exit!!!\n");
	s->status = TS_TERMINATED;
	pthread_exit(arc);
}




